﻿using jvm.instructions.common;
using jvm.rtda;
using jvm.rtda.frame;
using jvm.rtda.heap;
using jvm.rtda.heap.clazz;
using jvm.rtda.heap.clazz.cp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace jvm.instructions.references
{
    class PUT_FIELD : Index16Instruction
    {
        public override void Execute(JFrame frame)
        {
            JMethod method = frame.method;
            JClass curClass = method.jClass;
            JConstantPool pool = curClass.constantPool;
            FieldRef fieldRef = pool.GetConstant<FieldRef>(index);
            JField field = fieldRef.ResolvedField();

            // todo init class
            if (field.Is(AccessFlags.ACC_STATIC))
            {
                Console.WriteLine("java.lang.IncompatibleClassChangeError");
                Environment.Exit(0);
            }
            if (field.Is(AccessFlags.ACC_FINAL))
            {
                if (curClass != field.jClass || !method.name.Equals("<init>"))
                {
                    Console.WriteLine("java.lang.IllegalAccessError");
                    Environment.Exit(0);
                }
            }

            char descriptor = field.descriptor[0];
            uint slotId = field.slotId;
            JOperandStack stack = frame.operandStack;
            switch (descriptor)
            {
                case 'Z':
                case 'B':
                case 'C':
                case 'S':
                case 'I':
                    int valI = stack.PopInt();
                    JObject objI = stack.PopRef();
                    if (objI == null)
                    {
                        Console.WriteLine("java.lang.NullPointerException");
                        Environment.Exit(0);
                    }
                    objI.Fields.SetInt(slotId, valI);
                    break;
                case 'F':
                    float valF = stack.PopFloat();
                    JObject objF = stack.PopRef();
                    if (objF == null)
                    {
                        Console.WriteLine("java.lang.NullPointerException");
                        Environment.Exit(0);
                    }
                    objF.Fields.SetFloat(slotId, valF);
                    break;
                case 'J':
                    long valJ = stack.PopLong();
                    JObject objJ = stack.PopRef();
                    if (objJ == null)
                    {
                        Console.WriteLine("java.lang.NullPointerException");
                        Environment.Exit(0);
                    }
                    objJ.Fields.SetLong(slotId, valJ);
                    break;
                case 'D':
                    double valD = stack.PopDouble();
                    JObject objD = stack.PopRef();
                    if (objD == null)
                    {
                        Console.WriteLine("java.lang.NullPointerException");
                        Environment.Exit(0);
                    }
                    objD.Fields.SetDouble(slotId, valD);
                    break;
                case 'L':
                case '[':
                    JObject valR = stack.PopRef();
                    JObject objR = stack.PopRef();
                    if (objR == null)
                    {
                        Console.WriteLine("java.lang.NullPointerException");
                        Environment.Exit(0);
                    }
                    objR.Fields.SetRef(slotId, valR);
                    break;
            }
        }
    }
}
